﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace 综合同步
{
    public class NetTcpClient
    {
        IPEndPoint m_remoteEndPoint;
        TcpClient m_client = new TcpClient();

        public delegate void event_recvpack(EndPoint remotePoint, object info);
        public delegate void event_connected(EndPoint remotePoint);
        public delegate void event_disconnected(EndPoint remotePoint);
        public event event_connected envConnected;
        public event event_disconnected envDisConnected;
        public event event_recvpack envRecvPack;

        public NetTcpClient(string ip, ushort port, ushort local_port)
        {
            m_remoteEndPoint = new IPEndPoint(IPAddress.Parse(ip),port);
            Task.Run(new Action(() => {
                m_client.Client.Bind(new IPEndPoint(IPAddress.Any, local_port));
                m_client.Client.BeginConnect(ip, port, ConnectAsyn, m_client.Client);
            }));
        }
        public void Recv<T>(TcpClient client, T data)
        {
            envRecvPack?.Invoke(client.Client.LocalEndPoint,data);
        }
        void ConnectAsyn(IAsyncResult res)
        {
            try
            {
                m_client.Client.EndConnect(res);
                if (envConnected != null) envConnected(m_remoteEndPoint);
                ProtocolData recvProtocol = new ProtocolData(m_client, Recv);
                m_client.Client.BeginReceive(recvProtocol.RecvBuf, 0, recvProtocol.RecvBuf.Length, SocketFlags.None, 
                    RecieveDataAsyn, new List<object>() { m_client, recvProtocol });
            }
            catch (Exception)
            {
                if (envDisConnected != null) envDisConnected(m_remoteEndPoint);
                m_client.Client.BeginConnect(m_remoteEndPoint, ConnectAsyn, m_client.Client);
            }
        }
        void RecieveDataAsyn(IAsyncResult res)
        {
            List<object> state = (List<object>)res.AsyncState;
            TcpClient recClient = (TcpClient)state[0];
            ProtocolData recvProtocol = (ProtocolData)state[1];
            try
            {
                recvProtocol.LeftByte += recClient.Client.EndReceive(res);
                if (!recClient.Client.Connected||(recClient.Client.Poll(1000, SelectMode.SelectRead) && (recClient.Client.Available == 0)))
                {
                    if (envDisConnected != null) envDisConnected(m_remoteEndPoint);
                    recClient.Client.BeginConnect(m_remoteEndPoint, ConnectAsyn, recClient.Client);
                    return;
                }
                recvProtocol.RecvPackageData();
                int recvLength = recvProtocol.RecvBuf.Length - (recvProtocol.ProcessIndex + recvProtocol.LeftByte >= recvProtocol.RecvBuf.Length ?
                    recvProtocol.LeftByte : (recvProtocol.ProcessIndex + recvProtocol.LeftByte));
                recClient.Client.BeginReceive(recvProtocol.RecvBuf, (recvProtocol.ProcessIndex + recvProtocol.LeftByte) % recvProtocol.RecvBuf.Length,
                    recvLength,SocketFlags.None, RecieveDataAsyn, new List<object>() { recClient, recvProtocol });
            }
            catch (Exception)
            {
                m_client.Client.Close();
                m_client = new TcpClient();
                if (envDisConnected != null) envDisConnected(m_remoteEndPoint);
                m_client.Client.BeginConnect(m_remoteEndPoint, ConnectAsyn, m_client.Client);
            }
        }
        void SendDataAsyn(IAsyncResult res)
        {
            List<object> state = (List<object>)res.AsyncState;
            TcpClient sendClient = (TcpClient)state[0];
            byte[] sendData = (byte[])state[1];
            try
            {
                int ires = sendClient.Client.EndSend(res);
                if (!sendClient.Client.Connected||(sendClient.Client.Poll(1000, SelectMode.SelectRead) && (sendClient.Client.Available == 0)))
                {
                    return;
                }
                if (ires != sendData.Length) System.Diagnostics.Trace.WriteLine("数据发送失败");
            }
            catch (Exception)
            {

            }
        }
        public void SendData(byte[] sendData)
        {
            try
            {
                if(m_client.Client.Connected)
                    m_client.Client.BeginSend(sendData, 0, sendData.Length, SocketFlags.None, SendDataAsyn, new List<object> { m_client, sendData });
            }
            catch (Exception)
            {
            }
        }
    }
}
